home *** CD-ROM | disk | FTP | other *** search
- ///////////////////////////////////////////////////////////////////////////////
- // FILENAME: eTImageComponent.m
- // SUMMARY: Implementation for a container of imageable data.
- // SUPERCLASS: Object
- // INTERFACE: None, but it "exports" eTImageComponentIcon.
- // PROTOCOLS: <ComponentData,
- // ETFDSupport,HTMDSupport,LaTeXSupport>
- // AUTHOR: Rohit Khare
- // COPYRIGHT: (c) 1994 California Institure of Technology, eText Project
- ///////////////////////////////////////////////////////////////////////////////
- // IMPLEMENTATION COMMENTS
- // The concept is that eText systems only access NXImages through this
- // component, which offers default services for image coercion and encoding.
- //
- // The UI has the responsibility for passing in the desired filespec.
- // Thus to keep a .pcx file as .pcx, pass in a complete path to the .pcx
- // on disk and eText will either reference it or copy it as-is into the
- // future (without filter-services, you will get eTImageComponentIcon.tiff)
- // If the file cannot be found (or isDirty), eText will generate a .tiff
- // and reset the names correctly.
- //
- // Since this component maps onto an IMG tag, the only format with
- // wide acceptance for IMG is GIF; so any non-gif file will be coerced.
- // Similarly, LaTeX only has provisions for eps, so it will, too.
- //
- // LaTeX and HTML representations will force the creation of local copies of
- // images (this can/will change in the future, cf HTML OODB ideas)
- //
- // shared images are currently envisioned for sysBitmaps like NXLinkButton
- //
- // Note on HTML calling convention: this object maps exclusively onto the
- // IMG tag, which can occur in the body of another anchor-like element.
- ///////////////////////////////////////////////////////////////////////////////
- // HISTORY
- // 01/22/95: Added call to handle caption strings.
- // 12/23/94: Patched to always reset etDoc on read/write
- // 07/19/94: Rearchitected
- // 07/14/94: Created. Aliases the NeXTSTEP NXImage API.
- ///////////////////////////////////////////////////////////////////////////////
-
- #import "eTImageComponent.h"
- #import "Kludges.subproj/EPSWriting.h"
- #import "Kludges.subproj/GIFWriting.h"
- #define _eTImageComponentVERSION 10
-
- @implementation eTImageComponent
- // NXImage *theImage;
- // BOOL isShared;
-
- + newImageNamed:(const char *)newName
- {
- static id _imageComponentTable=nil;
- id newObj;
- id newImg;
- NXAtom theName;
-
- theName = NXUniqueString(newName);
- if (!_imageComponentTable) {
- _imageComponentTable = [[HashTable alloc] initKeyDesc:"%"];
- }
- newObj = [_imageComponentTable valueForKey:theName];
- if (!newObj) {
- if (newImg = [NXImage findImageNamed:theName]){ // system bitmap?
- [newImg setScalable:NO]; // this is a shared image!
- [newImg setDataRetained:YES]; // write TIFFs of sysBitmaps
- [newImg setEPSUsedOnResolutionMismatch:YES];
- // Here's a hack for dealing with recalcitrant alpha channels:
- [newImg setBackgroundColor:NX_COLORWHITE];
- } else {
- newImg = [[NXImage alloc] init];
- if (![newImg useFromFile:theName]){
- [newImg free];
- newImg = [NXImage findImageNamed:"eTImageComponentIcon"];
- } else {
- // WHAT Happens to naming if theName is an absolute path?
- [newImg setName:theName];
- }
- }
-
- newObj = [[[eTImageComponent alloc] initInDoc:nil linked:NO]
- useImage:newImg name:theName path:"" shared:YES];
- [_imageComponentTable insertKey:theName value:newObj];
- }
- return newObj;
- }
- - theImage {return theImage;}
- - setComponentName:(const char*)newComponentName
- {return (isShared ? nil : [super setComponentName:newComponentName]);}
- - (BOOL)isShared {return isShared;}
- - (BOOL)isMutable {return (!isShared && [super isMutable]);}
- //////// begin semi-private API section ///////
- - initInDoc:newDoc linked: (BOOL) linked
- {
- [super initInDoc:newDoc linked:linked];
- isShared = NO; shouldEdit = YES; theImage = nil;
- return self;
- }
- - free
- {
- if (isShared || [theImage name]) return self; // stop the free chain
- theImage = [theImage free];
- return self = [super free];
- }
-
- - useImage:(NXImage *)newImage
- name:(const char *) newComponentName
- path:(const char *) newCurrentPath
- shared:(BOOL) newShared;
- {
- theImage = newImage;
- componentName = NXUniqueString(newComponentName);
- currentPath = NXUniqueString(newCurrentPath);
- isShared = newShared;
- return self;
- }
-
- - readComponentFromPath:(const char *)newPath
- {
- if (newPath != currentPath)
- [super readComponentFromPath:newPath];
-
- // now we have filled in componentName/currentPath
- // go out to disk and find the image
- theImage = [[NXImage alloc] init];
- [theImage setScalable:YES];
- [theImage setDataRetained:YES];
- [theImage setEPSUsedOnResolutionMismatch:YES];
- // Here's a hack for dealing with recalcitrant alpha channels:
- [theImage setBackgroundColor:NX_COLORWHITE];
-
- if (![theImage loadFromFile:currentPath]){
- char temp[MAXPATHLEN];
-
- strcpy(temp, currentPath);
- strcat(temp, ".eps");
- if (![theImage loadFromFile:temp]){
- strcpy(temp, currentPath);
- strcat(temp, ".tiff");
- if (![theImage loadFromFile:temp]){
- strcpy(temp, currentPath);
- [theImage free];
- // use eTImageComponentIcon.tiff to indicate an error
- theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
- isShared = YES;
- }
- }
- currentPath = NXUniqueString(temp);
- }
- return self;
- }
- - writeComponentToPath:(NXAtom)path inFormat:(int) theFormat
- {
- const char *tmp;
- char target[MAXPATHLEN];
-
- if ((theFormat == ETFD_FMT) && (isShared || [theImage name]))
- return self;
- switch(theFormat) {
- case ETFD_FMT:
- // if it's not (a link) or (clean and the target is found),
- // if the (source is found and it's clean), copy
- // else write.
- if (isShared) break; // special case for etfds.
- sprintf(target,"%s/%s",path,componentName);
- if (!((isLinked) || ((!isDirty) && (!access(target,F_OK|R_OK))))){
- if ((!isDirty) && *currentPath &&
- (!access(currentPath, F_OK|R_OK))) {
- // copy
- char cmd[2*MAXPATHLEN];
-
- sprintf(cmd,"cp -rp \"%s\" \"%s\"", currentPath, target);
- system(cmd);
- currentPath = NXUniqueString(target);
- } else {
- id repList;
- int i,j;
- BOOL foundEPS=NO;
- NXStream *stream;
-
- // choose format
- repList = [theImage representationList];
- i = [repList count];
- for(j=0; ((j<i) && !foundEPS); j++){
- if ([[repList objectAt:j] isKindOf:[NXEPSImageRep class]])
- foundEPS = YES;
- }
-
- stream = NXOpenMemory(NULL, 0, NX_READWRITE);
- if (foundEPS) {
- [theImage writeEPS:stream];
- if (!rindex(target, '.') ||
- strcasecmp(".eps", rindex(target, '.')))
- strcat(target, ".eps");
- } else {
- [theImage writeTIFF:stream allRepresentations:YES];
- if (!rindex(target, '.') ||
- strncasecmp(".tif", rindex(target, '.'),3))
- strcat(target, ".tiff");
- }
- NXSaveToFile(stream, target);
- NXCloseMemory(stream, NX_FREEBUFFER);
- currentPath = NXUniqueString(target);
- componentName = NXUniqueString(rindex(target,'/')+1);
- }
- isDirty=NO;
- }
- [etDoc registerComponent:componentName];
- break;
- case HTMD_FMT:
- // Always is a gif. If it doesn't exist, copy a .gif or create one.
- if (componentName) tmp = componentName;
- else if ([theImage name]) tmp = [theImage name];
- else tmp = [[self class] name];
-
- sprintf(target,"%s/%s",path,tmp);
- if(!rindex(target, '.') || strcasecmp(".gif", rindex(target, '.')))
- strcat(target, ".gif");
- if (access(target,F_OK|R_OK) || isDirty) {
- // if currentPath is a gif, copy.
- if (rindex(currentPath, '.') &&
- (!strcasecmp(".gif", rindex(currentPath, '.'))) &&
- (!access(currentPath,F_OK|R_OK))) {
- char cmd[2*MAXPATHLEN];
-
- sprintf(cmd,"cp -rp \"%s\" \"%s\"", currentPath, target);
- system(cmd);
- } else {
- [theImage writeGIFtoPath:target];
- }
- }
- [etDoc registerComponent:(rindex(target,'/')+1)];
- break;
- case TeXD_FMT:
- // Always is an eps. If it doesn't exist, copy a .eps or create one.
- if (componentName) tmp = componentName;
- else if ([theImage name]) tmp = [theImage name];
- else tmp = [[self class] name];
- sprintf(target,"%s/%s",path,tmp);
- if (!rindex(target, '.') || strcasecmp(".eps", rindex(target, '.')))
- strcat(target, ".eps");
- if (access(target,F_OK|R_OK) || isDirty) {
- // if currentPath is an eps, copy.
- if ((rindex(currentPath, '.')) &&
- (!strcasecmp(".eps", rindex(currentPath, '.'))) &&
- (!access(currentPath,F_OK|R_OK))) {
- char cmd[2*MAXPATHLEN];
-
- sprintf(cmd,"cp -rp \"%s\" \"%s\"", currentPath, target);
- system(cmd);
- } else {
- NXStream *stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
- [theImage writeEPS:stream];
- NXSaveToFile(stream, target);
- NXCloseMemory(stream, NX_FREEBUFFER);
- }
- }
- [etDoc registerComponent:(rindex(target,'/')+1)];
- break;
- }
- return self;
- }
-
- - writeComponentToPboard:thePboard
- {
- // I don't know why yet, but I want to defeat dragging of shared images
- return (isShared ? nil : [super writeComponentToPboard:thePboard]);
- }
- - addToPboard:pboard
- {
- id repList;
- int i,j;
- BOOL foundEPS=NO;
- NXStream *stream;
-
- // we can let people copy them, though.???
- if (!theImage) return nil;
-
- // choose format
- repList = [theImage representationList];
- i = [repList count];
- for(j=0; ((j<i) && !foundEPS); j++){
- if ([[repList objectAt:j] isKindOf:[NXEPSImageRep class]])
- foundEPS = YES;
- }
-
- stream = NXOpenMemory(NULL, 0, NX_READWRITE);
- if (foundEPS) {
- [theImage writeEPS:stream];
- [pboard addTypes:&NXPostScriptPboardType num:1 owner:nil];
- [pboard writeType:NXPostScriptPboardType fromStream:stream];
- } else {
- [theImage writeTIFF:stream allRepresentations:YES];
- [pboard addTypes:&NXTIFFPboardType num:1 owner:nil];
- [pboard writeType:NXTIFFPboardType fromStream:stream];
- }
- NXCloseMemory(stream, NX_FREEBUFFER);
- return self;
- }
- - readComponentFromPboard:thePboard
- {
- [super readComponentFromPboard:thePboard];
- // ok, so now we have a quasi-valid entry in currentPath
- // If the image can init from thePboard, go right ahead.
-
- theImage = [[NXImage alloc] init];
- [theImage setScalable:YES];
- [theImage setDataRetained:YES];
- [theImage setEPSUsedOnResolutionMismatch:YES];
- // Here's a hack for dealing with recalcitrant alpha channels:
- [theImage setBackgroundColor:NX_COLORWHITE];
-
- if ([NXImage canInitFromPasteboard:thePboard]) {
- // Patch for recalcitrant f*king appkit
- if([NXEPSImageRep canInitFromPasteboard:thePboard]) {
- id epsRep = [[NXEPSImageRep alloc] initFromPasteboard:thePboard];
- [theImage useRepresentation:epsRep];
- } else
- theImage = [theImage initFromPasteboard:thePboard];
- if (!theImage){
- theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
- isShared = YES;
- }
- // special case: If we paste raw image data and there's no path,
- // componentName is currently "Untitled-%d"; we need to cat an ext.
- // we should look at how this whole schmeer works/fails with RIBs.
- if (!(currentPath && *currentPath)) {
- char tmp[MAXPATHLEN];
-
- strcpy(tmp, componentName);
- if([thePboard findAvailableTypeFrom:&NXPostScriptPboardType num:1])
- strcat(tmp, ".eps");
- else if([thePboard findAvailableTypeFrom:&N3DRIBPboardType num:1])
- strcat(tmp, ".rib");
- else strcat(tmp, ".tiff");
- componentName = NXUniqueString(tmp);
- }
- } else if (currentPath && *currentPath) {
- if (![theImage loadFromFile:currentPath]){
- [theImage free];
- theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
- isShared = YES;
- }
- } else {
- theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
- isShared = YES;
- }
- return self;
- }
-
- - readRichText:(NXStream *)stream forView:view
- {
- int i;
- id temp;
-
- temp = [view etDoc];
- if (temp && [temp respondsTo:@selector(registerComponent:)])
- etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
- NXScanf(stream, "%d ", &i);
- if (i != _eTImageComponentVERSION) {
- // bad version block.
- NXLogError("eTImageComponent found unparseable version %d at position %d",
- i, NXTell(stream));
- return nil;
- }
- NXScanf(stream, "%c ", &isShared); isShared -= 'A';
- [super readRichText:stream forView:view];
- if (isShared){
- // we have a real problem here: we need to return the "uniq"d instance!
-
- id retval = [eTImageComponent newImageNamed:componentName];
- [self free];
- return retval;
- }
- if (!isLinked) {
- // derive a fresh currentPath. try to load.
- char buf[MAXPATHLEN];
-
- sprintf(buf, "%s/%s",[[etDoc docInfo] docPath], componentName);
- currentPath = NXUniqueString(buf);
- }
- [self readComponentFromPath:currentPath];
- return self;
- }
- - writeRichText:(NXStream *)stream forView:view
- {
- BOOL fakeShared;
- id temp;
-
- temp = [view etDoc];
- if (temp && [temp respondsTo:@selector(registerComponent:)])
- etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
- // this fixes a subtle bug related to images we can't locate at
- // read-time and are assigned the eTImageComponentIcon by default
- // (and thus set isShared) -- on reopen, it calls +newImageNamed with
- // the old componentName
- fakeShared = isShared;
- if (([theImage name] == NXUniqueString("eTImageComponentIcon")) &&
- strcmp([theImage name], componentName))
- fakeShared = NO;
- NXPrintf(stream, "%d %c ", _eTImageComponentVERSION, fakeShared+'A');
- [super writeRichText:stream forView:view];
- return self;
- }
-
- - writeHTML:(NXStream *)stream forView:view {
- return [self writeHTML:(NXStream *)stream forView:view withCaption:NULL];}
- - writeHTML:(NXStream *)stream forView:view withCaption:(NXAtom)caption
- {
- const char *tmp;
- id temp;
-
- temp = [view etDoc];
- if (temp && [temp respondsTo:@selector(registerComponent:)])
- etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
- if (componentName) tmp = componentName;
- else if ([theImage name]) tmp = [theImage name];
- else tmp = [[self class] name];
- NXPrintf(stream, "<IMG SRC=\"%s%s\" ALT=\"%v\">", tmp,
- ((!rindex(tmp, '.') || strcasecmp(".gif", rindex(tmp, '.')))?".gif":""),
- (caption && *caption) ? caption : tmp);
- return self;
- }
-
-
- - writeLaTeX:(NXStream *)stream forView:view
- {
- const char *tmp;
- id temp;
-
- temp = [view etDoc];
- if (temp && [temp respondsTo:@selector(registerComponent:)])
- etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
- if (componentName) tmp = componentName;
- else if ([theImage name]) tmp = [theImage name];
- else tmp = [[self class] name];
- NXPrintf(stream, "\n\\centerline{\\epsffile{%s%s}}\n",tmp,
- ((!rindex(tmp, '.') || strcasecmp(".eps", rindex(tmp, '.')))?".eps":""));
- return self;
- }
-
- // VERY PRIVATE API
- - setImage:newImg {theImage = newImg; return self;}
- - setShared:(BOOL)newState {isShared = newState; return self;}
- - registerError
- {
- theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
- isShared = YES;
- return self;
- }
- @end